package io.hellsinger.vortex.ui.component.storage

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.RippleDrawable
import android.util.AttributeSet
import android.view.Gravity
import android.view.ViewGroup
import android.widget.Button
import android.widget.FrameLayout
import androidx.annotation.FloatRange
import io.hellsinger.vortex.R
import io.hellsinger.vortex.ui.component.ViewMeasure
import io.hellsinger.vortex.ui.component.dp
import io.hellsinger.vortex.ui.component.drawable.RoundedCornerDrawable
import io.hellsinger.vortex.ui.component.sp

class StorageSnackBar : FrameLayout {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyleAttr: Int,
    ) : super(
        context,
        attrs,
        defStyleAttr,
    )

    var state: Int = IDLE
        private set

    private val shape = RoundedCornerDrawable(0xFF404040, 8F.dp)

    private val indicatorPaint =
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            style = Paint.Style.STROKE
            strokeWidth = 1.5F.dp
        }

    private val trackBounds =
        RectF(
            16F.dp,
            8F.dp,
            16F.dp + (16F.dp * 2),
            height - 8F.dp,
        )

    private val trackPaint =
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            style = Paint.Style.STROKE
            strokeWidth = 1F.dp
        }

    private var indicatorAnimator: ValueAnimator? = null

    private val unitBounds = Rect()

    private val unitPaint =
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            style = Paint.Style.FILL
            textSize = 12F.sp
        }

    private val titleBounds = Rect()

    private val titlePaint =
        Paint(Paint.ANTI_ALIAS_FLAG).apply {
            style = Paint.Style.FILL
            textSize = 16F.sp
        }

    private val action =
        Button(context).apply {
            text = "Cancel"
            background =
                RippleDrawable(
                    ColorStateList.valueOf(0x326200EE),
                    null,
                    RoundedCornerDrawable(0, 16F.dp),
                )
            layoutParams =
                LayoutParams(
                    LayoutParams.WRAP_CONTENT,
                    LayoutParams.WRAP_CONTENT,
                ).apply {
                    marginEnd = 8.dp
                    gravity = Gravity.CENTER or Gravity.END
                }
        }

    @FloatRange(from = -1.0, to = 1.0)
    var progress: Float = 0F
    var unit: String? = null
        set(value) {
            if (value == field) return
            field = value
            if (!value.isNullOrEmpty()) unitPaint.getTextBounds(value, 0, value.length, unitBounds)
            invalidate()
        }
    var title: String? = null
        set(value) {
            if (value == field) return
            field = value
            if (!value.isNullOrEmpty()) unitPaint.getTextBounds(value, 0, value.length, titleBounds)
            invalidate()
        }

    fun updateProgress(progress: Float) {
        if (this.progress == progress) return
        if (progress > 1.0F) this.progress = 1F
        createIndicatorAnimator(progress).start()
    }

    inline fun updateProgress(new: (Float) -> Float) {
        updateProgress(new(progress))
    }

    private fun getUnitWidth(): Int = if (unit.isNullOrEmpty()) 0 else unit!!.length.dp

    private fun getTitleWidth(): Int = if (title.isNullOrEmpty()) 0 else title!!.length.dp

    fun setAction(
        title: String,
        onClick: OnClickListener,
    ) {
        action.text = title
        action.setOnClickListener(onClick)
    }

    init {
        id = R.id.file_manager_list_snack_bar_root
        background = shape

        addView(action)
    }

    override fun onMeasure(
        widthMeasureSpec: Int,
        heightMeasureSpec: Int,
    ) {
        ViewMeasure(
            widthSpec = widthMeasureSpec,
            heightSpec = heightMeasureSpec,
            desireWidth = ViewGroup.LayoutParams.MATCH_PARENT,
            desireHeight = 48,
            onResult = ::setMeasuredDimension,
        )

        measureChild(action, widthMeasureSpec, heightMeasureSpec)
    }

    override fun onDraw(canvas: Canvas) {
        if (progress == -1F) {
            if (!title.isNullOrEmpty()) {
                canvas.drawText(
                    title!!,
                    16F.dp,
                    (height / 2F) - titleBounds.centerY(),
                    titlePaint,
                )
            }
            return
        }

//        if (!isVisible) {
        canvas.drawArc(
            trackBounds,
            0F,
            360F,
            false,
            trackPaint,
        )

        canvas.drawArc(
            trackBounds,
            0F,
            progress * 360F,
            false,
            indicatorPaint,
        )

        if (!unit.isNullOrEmpty()) {
            canvas.drawText(
                unit!!,
                trackBounds.centerX() - unitBounds.centerX(),
                trackBounds.centerY() - unitBounds.centerY(),
                unitPaint,
            )
        }

        if (!title.isNullOrEmpty()) {
            canvas.drawText(
                title!!,
                trackBounds.right + 16.dp,
                height / 2F - titleBounds.centerY(),
                titlePaint,
            )
        }
//        }
    }

    private fun createIndicatorAnimator(to: Float): ValueAnimator {
        if (indicatorAnimator != null) indicatorAnimator!!.cancel()
        indicatorAnimator = ValueAnimator.ofFloat(progress, to)
        indicatorAnimator!!.addUpdateListener { animator ->
            progress = animator.animatedValue as Float
            invalidate()
        }
        indicatorAnimator!!.duration = 300L
        indicatorAnimator!!.addListener(
            object : AnimatorListenerAdapter() {
                override fun onAnimationCancel(animation: Animator) {
                    progress = to
                    indicatorAnimator = null
                }

                override fun onAnimationEnd(animation: Animator) {
                    progress = to
                    indicatorAnimator = null
                }
            },
        )

        return indicatorAnimator!!
    }

    fun show() {
        if (state == SCROLLED_UP) return
        state = SCROLLED_UP
        val y = 0F
        animate().setDuration(ANIMATION_DURATION).translationY(y)
    }

    fun hide() {
        if (state == SCROLLED_DOWN) return
        state = SCROLLED_DOWN
        val y = height.toFloat() + (layoutParams as MarginLayoutParams).bottomMargin
        animate().setDuration(ANIMATION_DURATION).translationY(y)
    }

    fun showAndHideAfter(delay: Long) {
        show()
        postDelayed({ hide(delay) }, ANIMATION_DURATION)
    }

    fun hide(delay: Long) {
        postDelayed(::hide, delay)
    }

    companion object {
        const val IDLE = -1
        const val SCROLLED_UP = 0
        const val SCROLLED_DOWN = 1

        const val ANIMATION_DURATION = 300L
    }
}
